#include <stdio.h>
#include <stdlib.h>
#include <ulog/ulog.h>
#include "record.h"
#include "cx_hal.h"
#include "cx_utility.h"
#include "cx_debug.h"
#include <dirent.h>

#define TAG "cx_record_video"

static long long time_record_start;
static int video_current_segment_len = 0;
static int audio_current_segment_len = 0;
static long current_segment_index_pos;
static long long current_segment_start_time;
static int clear_rb_flag;
static pthread_t thread_audio; 
static pthread_t thread_video; 
static pthread_t thread_save_file; 

static int record_segment_save(void *hdl, long long start_time, unsigned int duration)
{
    long current_index_pos;
    char video_file_name[MAX_RECORD_NAME_LEN];
    char audio_file_name[MAX_RECORD_NAME_LEN];

    cx_record_priv_t *priv = (cx_record_priv_t *)hdl;
    fwrite(&priv->crc_index, 1, sizeof(priv->crc_index), priv->fp_record_index);
    current_index_pos = ftell(priv->fp_record_index);
    cx_record_index_segment_head segment_head;
    segment_head.start_time = start_time;
    segment_head.duration = duration;
    segment_head.next_segment_pos = current_index_pos;
    fseek(priv->fp_record_index, current_segment_index_pos, SEEK_SET);
    fwrite(&segment_head, 1, sizeof(segment_head), priv->fp_record_index);
    fseek(priv->fp_record_index, current_index_pos, SEEK_SET);

    fwrite(&priv->crc_video, 1, sizeof(priv->crc_video), priv->fp_video);
    fwrite(&priv->crc_audio, 1, sizeof(priv->crc_audio), priv->fp_audio);
    fclose(priv->fp_video);
    fclose(priv->fp_audio);
    LOGD(TAG, "%s start_time:%lld duration:%d pos:%d %ld", __func__, segment_head.start_time, segment_head.duration, segment_head.next_segment_pos, current_segment_index_pos);

    current_segment_index_pos = current_index_pos;
    priv->record_segment++;
    memset(&segment_head, 0, sizeof(segment_head));
    fwrite(&segment_head, 1, sizeof(segment_head), priv->fp_record_index);
    time(&current_segment_start_time);
    snprintf(video_file_name, MAX_RECORD_NAME_LEN, "%s_%02d.h264", priv->record_raw_data_file_name, priv->record_segment);
    snprintf(audio_file_name, MAX_RECORD_NAME_LEN, "%s_%02d.g711", priv->record_raw_data_file_name, priv->record_segment);
    priv->fp_video = fopen(video_file_name, "w");
    priv->fp_audio = fopen(audio_file_name, "w");
    if((!priv->fp_video) || (!priv->fp_audio)) {
        LOGE(TAG, "create record file fail\n");
        return -1;
    }
    LOGD(TAG, "new segment %s %s index pos:%ld", video_file_name, audio_file_name, ftell(priv->fp_record_index));
    priv->video_size+=video_current_segment_len;
    video_current_segment_len = 0;
    priv->audio_size+=audio_current_segment_len;
    audio_current_segment_len = 0;
    priv->crc_index = 0;
    priv->crc_video = 0;
    priv->crc_audio = 0;
    return 0;
}

static void *record_save_file_entry(void *paras)
{
    int ret, read_size;
    void *status = NULL;
    cx_record_priv_t *priv = (cx_record_priv_t *)paras;
    uint8_t buff[1024];
    int block = 1024;
    int busy_flag;

    while(1) {;
        busy_flag = 0;
        if((cx_util_ringbuffer_available_read_space(&priv->record_video_rb) >= block) || (priv->task_running_flag == 0) || clear_rb_flag) {
            read_size = cx_util_ringbuffer_read(&priv->record_video_rb, buff, block);
            if(read_size > 0) {
                ret = fwrite(buff, 1, read_size, priv->fp_video);
                if(ret != read_size) {
                    LOGE(TAG, "%s %d fwrite error %d=%d\n", __func__, __LINE__, ret, read_size);
                }
                priv->crc_video = cx_util_crc16(priv->crc_video, buff, ret);
                busy_flag = 1;
            }
        }
        if((cx_util_ringbuffer_available_read_space(&priv->record_audio_rb) >= block) || (priv->task_running_flag == 0) || clear_rb_flag) {
            read_size = cx_util_ringbuffer_read(&priv->record_audio_rb, buff, block);
            if(read_size > 0) {
                ret = fwrite(buff, 1, read_size, priv->fp_audio);
                if(ret != read_size) {
                    LOGE(TAG, "%s %d fwrite error %d=%d\n", __func__, __LINE__, ret, read_size);
                }
                priv->crc_audio = cx_util_crc16(priv->crc_audio, buff, ret);
                busy_flag = 1;
            }
        }  
        if(priv->task_running_flag == 0 && cx_util_ringbuffer_empty(&priv->record_video_rb) && cx_util_ringbuffer_empty(&priv->record_audio_rb)) {
            fwrite(&priv->crc_video, 1, sizeof(priv->crc_video), priv->fp_video);
            fwrite(&priv->crc_audio, 1, sizeof(priv->crc_audio), priv->fp_audio);
            LOGD(TAG, "record_save_file task quit peak:%d/%d %d/%d", priv->record_video_rb_peak, priv->record_video_rb.length, priv->record_audio_rb_peak, priv->record_audio_rb.length);
            break;
        }
        if(busy_flag == 0) {
            cx_sleep_ms(10);
        }
    }
    pthread_exit(&status);
    return NULL;
}

static void *record_video_entry(void *paras)
{
    int ret;
    int len = 0;
	int is_iframe;
    void *status = NULL;
    long long time_stamp_video, time_stamp_video_last = 0;
    cx_record_priv_t *priv = (cx_record_priv_t *)paras;

    uint8_t *encoded_data = malloc(RECORD_VIDEO_FRAME_MAX_SIZE);
    if(!encoded_data) {
        LOGE(TAG, "%s %d malloc fail\n", __func__, __LINE__);
        goto exit2;
    }
    cx_record_index_frame video_index_frame;
    video_index_frame.head = RECORD_INDEX_FRAME_HEAD;
    priv->crc_video = 0;
    while(priv->task_running_flag) {
        len = cx_get_encoded_video(encoded_data, RECORD_VIDEO_FRAME_MAX_SIZE, &is_iframe, &time_stamp_video);
        if(len > 0) {
            if(time_record_start == 0) {
                time_record_start = time_stamp_video;
            }
            if(priv->save_type == CX_RECORD_VIDEO_SAVE_BY_FRAME) {
                if(video_current_segment_len + len > RECORD_VIDEO_SEGMENT_MAX_SIZE) {
                    while(!cx_util_ringbuffer_empty(&priv->record_video_rb) || !cx_util_ringbuffer_empty(&priv->record_audio_rb)) {
                        clear_rb_flag = 1;
                        LOGE(TAG, "wait rb empty %d %d", cx_util_ringbuffer_available_read_space(&priv->record_video_rb), cx_util_ringbuffer_available_read_space(&priv->record_audio_rb));
                        cx_sleep_ms(100);
                    }
                    clear_rb_flag = 0;
                    long long current_time;
                    time(&current_time);
                    pthread_mutex_lock(&priv->record_mutex);
                    record_segment_save(paras, current_segment_start_time, (current_time-current_segment_start_time)*1000);
                    pthread_mutex_unlock(&priv->record_mutex);
                }
                priv->record_video_rb_peak =  (cx_util_ringbuffer_available_read_space(&priv->record_video_rb) > priv->record_video_rb_peak ? cx_util_ringbuffer_available_read_space(&priv->record_video_rb) :priv->record_video_rb_peak);
                if(cx_util_ringbuffer_available_write_space(&priv->record_video_rb) < len) {
                    LOGE(TAG, "record video rb full, drop frame");
                    continue;
                }
                cx_util_ringbuffer_write(&priv->record_video_rb, encoded_data, len);
                video_index_frame.media_type = 0;
                video_index_frame.key_frame_flag = is_iframe;
                video_index_frame.offset = video_current_segment_len;
                video_index_frame.len = len;
                video_index_frame.time_stamp = time_stamp_video - time_record_start;
                video_current_segment_len+=len;
                pthread_mutex_lock(&priv->record_index_mutex);
                ret = fwrite(&video_index_frame, 1, sizeof(video_index_frame), priv->fp_record_index);
                if(ret != sizeof(video_index_frame)) {
                    LOGE(TAG, "%s %d fwrite error %d %d\n", __func__, __LINE__, ret, sizeof(video_index_frame));
                }
                priv->crc_index = cx_util_crc16(priv->crc_index, (unsigned char *)&video_index_frame, sizeof(video_index_frame));
                pthread_mutex_unlock(&priv->record_index_mutex);
                if(((time_stamp_video - time_stamp_video_last) > 60) && (time_stamp_video_last != 0)) {
                    LOGE(TAG, "video interval too big, %lld-%lld=%lld", time_stamp_video, time_stamp_video_last, time_stamp_video - time_stamp_video_last);
                }
                time_stamp_video_last = time_stamp_video;

            } else if(priv->save_type == CX_RECORD_VIDEO_SAVE_BY_GOP) {
                static int video_gop_len = 0;
                static uint8_t video_gop_data_save_buf[500*1024];
                static long long time_stamp_video_Iframe;
                static int video_gop_save_start = 0;
                if(is_iframe) {
                    if(video_gop_len > 0) {
                        pthread_mutex_lock(&priv->record_mutex);
                        time_stamp_video_Iframe = time_stamp_video;
                        long fp_video_cur_pos = ftell(priv->fp_video);
                        long fp_index_cur_pos = ftell(priv->fp_record_index);
                        if(fp_video_cur_pos != video_current_segment_len) {
                            LOGE(TAG, "%s pos:%ld %d\n", __func__, ftell(priv->fp_video), video_current_segment_len);
                            fseek(priv->fp_video, video_current_segment_len, SEEK_SET);
                            pthread_mutex_unlock(&priv->record_mutex);
                            goto save_new_gop;
                        }
                        ret = fwrite(video_gop_data_save_buf, 1, video_gop_len, priv->fp_video);
                        if((ret != video_gop_len) || (ftell(priv->fp_video) != (video_current_segment_len + video_gop_len))) {
                            LOGE(TAG, "%s %d fwrite error %d=%d %ld=%ld+%d %d\n", __func__, __LINE__, ret, video_gop_len, ftell(priv->fp_video), fp_video_cur_pos, video_gop_len, video_current_segment_len + video_gop_len);
                            fseek(priv->fp_video, fp_video_cur_pos, SEEK_SET);
                            pthread_mutex_unlock(&priv->record_mutex);
                            goto save_new_gop;
                        }
                        priv->crc_video = cx_util_crc16(priv->crc_video, video_gop_data_save_buf, video_gop_len);
                        video_index_frame.media_type = 0;
                        video_index_frame.key_frame_flag = is_iframe;
                        video_index_frame.offset = video_current_segment_len;
                        video_index_frame.len = video_gop_len;
                        video_index_frame.time_stamp = time_stamp_video_Iframe - time_record_start;
                        video_current_segment_len+=video_gop_len;
                        pthread_mutex_lock(&priv->record_index_mutex);
                        ret = fwrite(&video_index_frame, 1, sizeof(video_index_frame), priv->fp_record_index);
                        if(ret != sizeof(video_index_frame)) {
                            LOGE(TAG, "%s %d fwrite error %d %d\n", __func__, __LINE__, ret, sizeof(video_index_frame));
                            video_current_segment_len -= video_gop_len;
                            fseek(priv->fp_video, fp_video_cur_pos, SEEK_SET);
                            fseek(priv->fp_record_index, fp_index_cur_pos, SEEK_SET);
                        }
                        priv->crc_index = cx_util_crc16(priv->crc_index, (unsigned char *)&video_index_frame, sizeof(video_index_frame));
                        pthread_mutex_unlock(&priv->record_index_mutex);
                        pthread_mutex_unlock(&priv->record_mutex);
                    }
save_new_gop:
                    //新的I帧开始
                    video_gop_len = 0;
                    if(len > sizeof(video_gop_data_save_buf)) {
                        LOGE(TAG, "video I frame too long, discard frame, %d>%d\n", len, sizeof(video_gop_data_save_buf));
                        video_gop_save_start = 0;
                        continue;
                    }
                    memcpy(video_gop_data_save_buf, encoded_data, len);
                    video_gop_len+=len;
                    video_gop_save_start = 1;
                } else {
                    if(!video_gop_save_start)
                        continue;
                    if(video_gop_len > sizeof(video_gop_data_save_buf)) {
                        LOGE(TAG, "video gop data too long, discard frame, %d>%d\n", video_gop_len, sizeof(video_gop_data_save_buf));
                        continue;
                    }
                    memcpy(video_gop_data_save_buf+video_gop_len, encoded_data, len);
                    video_gop_len+=len;
                }
            }
        }
        cx_sleep_ms(30);
    }
    free(encoded_data);
exit2:
    pthread_exit(&status);
    return NULL;
}

static void *record_audio_entry(void *paras)
{
    int ret;
    void *status = NULL;
    long long time_stamp_audio, time_stamp_audio_last = 0;
    cx_record_priv_t *priv = (cx_record_priv_t *)paras;

    uint8_t *audio_compress_data = malloc(RECORD_AUDIO_FRAME_MAX_SIZE/2);
    if(!audio_compress_data) {
        LOGE(TAG, "%s %d malloc fail\n", __func__, __LINE__);
        goto exit;
    }
    int len = 0;
    cx_record_index_frame audio_index_frame;
    audio_index_frame.head = RECORD_INDEX_FRAME_HEAD;
    priv->crc_audio = 0;
    while(priv->task_running_flag) {
        len = cx_get_encoded_audio(audio_compress_data, &time_stamp_audio);
        if(len > 0) {
            if(time_record_start == 0) {
                time_record_start = time_stamp_audio;
            }
            if(clear_rb_flag) {
                continue;
            }
            priv->record_audio_rb_peak =  cx_util_ringbuffer_available_read_space(&priv->record_audio_rb) > priv->record_audio_rb_peak ? cx_util_ringbuffer_available_read_space(&priv->record_audio_rb) :priv->record_audio_rb_peak;
            if(cx_util_ringbuffer_available_write_space(&priv->record_audio_rb) < len) {
                LOGE(TAG, "record audio full, drop frame");
                continue;
            }
            pthread_mutex_lock(&priv->record_mutex); 
            cx_util_ringbuffer_write(&priv->record_audio_rb, audio_compress_data, len);
            audio_index_frame.media_type = 1;
            audio_index_frame.key_frame_flag = 0;
            audio_index_frame.offset = audio_current_segment_len;
            audio_index_frame.len = len;
            audio_index_frame.time_stamp = time_stamp_audio - time_record_start;
            audio_current_segment_len+=len;
            pthread_mutex_lock(&priv->record_index_mutex);
            ret = fwrite(&audio_index_frame, 1, sizeof(audio_index_frame), priv->fp_record_index);
            if(ret != sizeof(audio_index_frame)) {
                LOGE(TAG, "%s %d fwrite error %d %d\n", __func__, __LINE__, ret, sizeof(audio_index_frame));
            }
            priv->crc_index = cx_util_crc16(priv->crc_index, (unsigned char *)&audio_index_frame, sizeof(audio_index_frame));
            pthread_mutex_unlock(&priv->record_index_mutex);
            if(((time_stamp_audio - time_stamp_audio_last) > 60) && (time_stamp_audio_last != 0)) {
                LOGE(TAG, "audio interval too big, %lld-%lld=%lld", time_stamp_audio, time_stamp_audio_last, time_stamp_audio-time_stamp_audio_last);
            }
            time_stamp_audio_last = time_stamp_audio;
            pthread_mutex_unlock(&priv->record_mutex); 
        }
        cx_sleep_ms(30);
    }
    free(audio_compress_data);
exit:
    pthread_exit(&status);
    return NULL;
}

int cx_vr_record_start(void *hdl, int cam_id, uint8_t video_name[128], uint32_t timeout)
{
    CHECK_PARAM(hdl && video_name, -1);
    cx_record_priv_t *priv = (cx_record_priv_t *)hdl;
    int ret;
#ifdef CX_VR_LOWEST_STORAGE
    int free_percent;
    struct statfs fsinfo;
    ret = statfs(priv->root_path, &fsinfo);
    if (ret == 0) {
        free_percent = (int)((double)fsinfo.f_bfree * 100 / fsinfo.f_blocks);
        if (free_percent < CX_VR_LOWEST_STORAGE) {
            LOGE(TAG, "no enough room %d%%", free_percent);
            return 0;
        }
    }
#endif
    char device_id[128];
    char str_time[13];
    char str_date[7] = {0};
    char directory_name[MAX_RECORD_NAME_LEN];
    char data_index[MAX_RECORD_NAME_LEN];
    char video_file_name[MAX_RECORD_NAME_LEN];
    char audio_file_name[MAX_RECORD_NAME_LEN];
    time_t time_real;
    pthread_attr_t attr_video;
    pthread_attr_t attr_audio;
    pthread_attr_t attr_save_task;
    cx_record_index_head head_buf;
    cx_record_index_segment_head segment_head;

    if(priv->record_running_flag == 1) {
        LOGE(TAG, "record is running\n");
        return 0;
    }
    ret = cx_start_video_encode(1);
    if(ret < 0) {
        return 0;
    }
    ret = cx_start_audio_encode(1);
    if(ret < 0) {
        return 0;
    }
    priv->crc_index = 0;
    priv->crc_video = 0;
    priv->crc_audio = 0;
    priv->record_segment = 1;
    priv->video_size = 0;
    priv->audio_size = 0;
    memset(&head_buf, 0, sizeof(head_buf));
    priv->task_running_flag = 1;
    priv->record_running_flag = 1;
    time(&priv->start_time);
    time(&current_segment_start_time);

    /* check path /root/video/ */
    snprintf(directory_name, MAX_RECORD_NAME_LEN, "%s", priv->root_path);
    if (access(directory_name, 0) != 0) {
        mkdir(directory_name, 755);
    }
    snprintf(directory_name, MAX_RECORD_NAME_LEN, "%s/video", directory_name);
    if (access(directory_name, 0) != 0) {
        mkdir(directory_name, 755);
    }

    /* check path /root/video/yyMMdd/ */
    cx_util_localtime_print(str_time);
    memcpy(str_date, str_time, 6);
    snprintf(directory_name, 128, "%s/%s", directory_name, str_date);
    if (access(directory_name, 0) != 0) {
        mkdir(directory_name, 755);
    }
    cx_get_device_id(device_id);
    strncpy((char *)video_name, directory_name, MAX_RECORD_NAME_LEN);
    snprintf(data_index, sizeof(data_index), "%s/vrec_%s_%s.index", directory_name, device_id, str_time);
    snprintf(priv->record_raw_data_file_name, sizeof(priv->record_raw_data_file_name), "%s/vrec_%s_%s", directory_name, device_id, str_time);
    snprintf(video_file_name, sizeof(video_file_name), "%s_%02d.h264", priv->record_raw_data_file_name, priv->record_segment);
    snprintf(audio_file_name, sizeof(audio_file_name), "%s_%02d.g711", priv->record_raw_data_file_name, priv->record_segment);

    //记录录音数据及录音文件索引
    priv->fp_record_index = fopen(data_index, "w");
    priv->fp_video = fopen(video_file_name, "w");
    priv->fp_audio = fopen(audio_file_name, "w");
    if((!priv->fp_record_index) || (!priv->fp_video) || (!priv->fp_audio)) {
        LOGE(TAG, "create record file fail %s/%p %s/%p %s/%p\n", data_index, priv->fp_record_index, video_file_name, priv->fp_video, audio_file_name, priv->fp_audio);
        if(priv->fp_record_index) {
            fclose(priv->fp_record_index);
        }
        if(priv->fp_video) {
            fclose(priv->fp_video);
        }
        if(priv->fp_audio) {
            fclose(priv->fp_audio);
        }
        cx_start_video_encode(0);
        cx_start_audio_encode(0);
        priv->task_running_flag = 0;
        priv->record_running_flag = 0;
        return -1;
    }
    LOGD(TAG, "record start %s %dms save_type:%d\n", data_index, timeout, priv->save_type);
    time_record_start = 0;
    fwrite(&head_buf, 1, sizeof(head_buf), priv->fp_record_index);
    memset(&segment_head, 0, sizeof(segment_head));
    fwrite(&segment_head, 1, sizeof(segment_head), priv->fp_record_index);
    cx_video_seek_iframe(1100);
    video_current_segment_len = 0;
    audio_current_segment_len = 0;
    current_segment_index_pos = sizeof(cx_record_index_head);

    priv->record_video_ringbuffer = (char *)malloc(RECORD_VIDEO_RINGBUFFER_SIZE);
    cx_util_ringbuffer_create(&priv->record_video_rb, priv->record_video_ringbuffer, RECORD_VIDEO_RINGBUFFER_SIZE);
    priv->record_audio_ringbuffer = (char *)malloc(RECORD_AUDIO_RINGBUFFER_SIZE);
    cx_util_ringbuffer_create(&priv->record_audio_rb, priv->record_audio_ringbuffer, RECORD_AUDIO_RINGBUFFER_SIZE);
    pthread_mutex_init(&priv->record_mutex, NULL);
    pthread_mutex_init(&priv->record_index_mutex, NULL);

    struct sched_param shparam = {DEFAULT_THREAD_PRIORITY+1, 10};
    pthread_attr_init(&attr_video);
    pthread_attr_setschedparam(&attr_video, &shparam);
    pthread_attr_init(&attr_audio);
    pthread_attr_setschedparam(&attr_audio, &shparam);
    pthread_create(&thread_video, &attr_video, record_video_entry, priv);
    pthread_setname_np(thread_video, "record_video");
    pthread_create(&thread_audio, &attr_audio, record_audio_entry, priv);
    pthread_setname_np(thread_audio, "record_audio");
    struct sched_param shparam2 = {DEFAULT_THREAD_PRIORITY-5, 10};
    pthread_attr_init(&attr_save_task);
    pthread_attr_setschedparam(&attr_save_task, &shparam2);
    pthread_create(&thread_save_file, &attr_save_task, record_save_file_entry, priv);
    pthread_setname_np(thread_save_file, "record_save_task");

    while(1) {
        usleep(500000);
        time(&time_real);
        // LOGD(TAG, "recording %lldms..\n", time_exit-time_enter);
        if(priv->task_running_flag == 0) {
            return 0;
        }
        if(time_real > priv->start_time && (time_real-priv->start_time)*1000 >= timeout) {
            break;
        }
    }
    cx_vr_record_stop(hdl);
    return 0;
}

static int record_index_file_check(void *hdl)
{
    cx_record_priv_t *priv = (cx_record_priv_t *)hdl;

    char record_index_file_name[MAX_RECORD_NAME_LEN];
    snprintf(record_index_file_name, sizeof(record_index_file_name), "%s.index", priv->record_raw_data_file_name);
    FILE *file_index = fopen(record_index_file_name, "r");
    if(!file_index) {
        LOGE(TAG, "%s %d error", __func__, __LINE__);
        return -1;
    }
    cx_record_index_head index_head;
    cx_record_index_segment_head segment_head;
    cx_record_index_frame index_frame;
    int seg_index_len;
    uint16_t calc_crc = 0, raw_crc = 0;
    fread(&index_head, 1, sizeof(index_head), priv->fp_record_index);
    for(int i=0;i<index_head.segment_num;i++) {
        fread(&segment_head, 1, sizeof(segment_head), priv->fp_record_index);
        seg_index_len = segment_head.next_segment_pos - ftell(priv->fp_record_index);
        calc_crc = 0;
        for(int i=0; i< seg_index_len/sizeof(cx_record_index_frame); i++) {
            fread(&index_frame, 1, sizeof(index_frame), priv->fp_record_index);
            calc_crc = cx_util_crc16(calc_crc, (unsigned char *)&index_frame, sizeof(index_frame));
        }
        fread(&raw_crc, 1, sizeof(raw_crc), priv->fp_record_index);
        if(raw_crc != calc_crc) {
            fclose(file_index);
            LOGE(TAG, "%s %d error", __func__, __LINE__);
            return -1;
        }
        fseek(priv->fp_record_index, segment_head.next_segment_pos, SEEK_SET);
    }
    fclose(file_index);
    return 0;
}

int cx_vr_record_stop(void *hdl)
{
    CHECK_PARAM(hdl, -1);
    int ret;
    void *status = NULL;
    cx_record_priv_t *priv = (cx_record_priv_t *)hdl;
    if(priv->task_running_flag == 0) {
        LOGW(TAG, "record not running");
        return -1;
        
    }
    unsigned short crc_index_head;
    cx_record_index_head head_buf;

    priv->task_running_flag = 0;
    cx_start_video_encode(0);
    cx_start_audio_encode(0);
    // 等待 两个线程退出
    pthread_join(thread_audio, &status);
    pthread_join(thread_video, &status);
    pthread_join(thread_save_file, &status);

    cx_util_ringbuffer_destroy(&priv->record_video_rb);
    free(priv->record_video_ringbuffer);
    cx_util_ringbuffer_destroy(&priv->record_audio_rb);
    free(priv->record_audio_ringbuffer);
    priv->video_size+=video_current_segment_len;
    priv->audio_size+=audio_current_segment_len;

    fwrite(&priv->crc_index, 1, sizeof(priv->crc_index), priv->fp_record_index);

    cx_record_index_segment_head segment_head;
    long long current_time;
    time(&current_time);
    segment_head.start_time = current_segment_start_time;
    segment_head.duration = (current_time - current_segment_start_time)*1000;
    segment_head.next_segment_pos = ftell(priv->fp_record_index);
    fseek(priv->fp_record_index, current_segment_index_pos, SEEK_SET);
    fwrite(&segment_head, 1, sizeof(segment_head), priv->fp_record_index);

    priv->duration = (current_time - priv->start_time)*1000;
    memset(&head_buf, 0, sizeof(head_buf));
    head_buf.start_time = priv->start_time;
    head_buf.duration = priv->duration;
    head_buf.file_size = priv->video_size + priv->audio_size;
    head_buf.audio_param.audio_type = CX_AUDIO_FORMAT_G711A;
    head_buf.audio_param.channel = CX_AUDIO_CHANNEL_MONO;
    head_buf.audio_param.sample_bits = CX_AUDIO_SAMPLE_BITS_16BIT;
    head_buf.audio_param.sample_rate = 8000;
    head_buf.video_param.fps = 25;
    head_buf.video_param.video_type = CX_VIDEO_FORMAT_H264;
    head_buf.segment_num = priv->record_segment;
    head_buf.valid = 1;
    crc_index_head = cx_util_crc16(0, (unsigned char *)&head_buf, sizeof(cx_record_index_head))-2;
    head_buf.crc = crc_index_head;
    fseek(priv->fp_record_index, 0, SEEK_SET);
    fwrite(&head_buf, 1, sizeof(head_buf), priv->fp_record_index);
    fclose(priv->fp_record_index);
    fclose(priv->fp_video);
    fclose(priv->fp_audio);
    pthread_mutex_destroy(&priv->record_mutex);
    pthread_mutex_destroy(&priv->record_index_mutex);
    LOGD(TAG, "record over duration:%d video_len:%d audio_len:%d\n", priv->duration, priv->video_size, priv->audio_size);
    ret = record_index_file_check(hdl);
    if(ret < 0) {
        LOGE(TAG, "index file check error\n");
        char record_index_file_name[MAX_RECORD_NAME_LEN];
        snprintf(record_index_file_name, sizeof(record_index_file_name), "%s.index", priv->record_raw_data_file_name);
        unlink(record_index_file_name);
        for(int i=1; i <= priv->record_segment; i++) {
            snprintf(record_index_file_name, sizeof(record_index_file_name), "%s_%02d.h264", priv->record_raw_data_file_name, i);
            unlink(record_index_file_name);
            snprintf(record_index_file_name, sizeof(record_index_file_name), "%s_%02d.g711", priv->record_raw_data_file_name, i);
            unlink(record_index_file_name);
        }
    }
    priv->record_running_flag = 0;
    return 0;
}

int cx_vr_video_search(void *hdl, uint32_t start_time, uint32_t stop_time, cx_vr_video_info_t * info, int info_num)
{
    CHECK_PARAM(hdl && (start_time < stop_time) && info && (info_num >= 0), -1);
    int ret;
    cx_record_priv_t *priv = (cx_record_priv_t *)hdl;
    int record_num = 0;
    struct tm *info_start;
    char check_dir_name[MAX_RECORD_NAME_LEN];
    /* utc时间转化 */
    time_t time_tran = start_time;
    info_start = localtime(&time_tran);
    cx_util_utc_to_beijing(info_start);

    uint32_t start_time_cur_day = start_time - info_start->tm_hour*3600 - info_start->tm_min*60 - info_start->tm_sec;
    if((stop_time - start_time_cur_day) >= 24*60*60) {
        LOGE(TAG, "start_time stop_time not in one day");
        return -1;
    }
    sprintf(check_dir_name, "%s/video/%02d%02d%02d", priv->root_path, info_start->tm_year-2000, info_start->tm_mon, info_start->tm_mday);

    DIR *dp;
    struct dirent *out_dirent;
    char *destination = (char *)malloc(MAX_RECORD_NAME_LEN);
    dp = (DIR *)opendir(check_dir_name);

    if(!dp){
        LOGD(TAG, "%4d-%02d-%02d no record", info_start->tm_year, info_start->tm_mon, info_start->tm_mday);
        return 0;
    }
    cx_record_index_head head_buf;
    while (1) {
        out_dirent = readdir(dp);
        if(!out_dirent){
            break;
        }
        if(strstr(out_dirent->d_name, ".index") != NULL) {
            strncpy(destination, check_dir_name, MAX_RECORD_NAME_LEN);
            strcat(destination, "/");
            strcat(destination, out_dirent->d_name);
            FILE *fp = fopen(destination, "r");
            if(!fp)
                continue;
            ret = fread(&head_buf, 1, sizeof(head_buf), fp);
            fclose(fp);
            if(head_buf.valid) {
                if((head_buf.start_time >= start_time) && (head_buf.start_time <= stop_time)) {
                    info[record_num].size = head_buf.file_size;
                    info[record_num].type = CX_VIDEO_FORMAT_H264;
                    strncpy(info[record_num].name, destination, MAX_RECORD_NAME_LEN);
                    info[record_num].begine_time = head_buf.start_time;
                    info[record_num].end_time = head_buf.start_time + head_buf.duration/1000;
                    record_num++;
                    if(record_num >= info_num) {
                        break;
                    }
                }
            }
        }
    }
    closedir(dp);
    free(destination);
    return record_num;
}

int cx_vr_get_record_status(void *hdl) 
{
    CHECK_PARAM(hdl, -1);
    cx_record_priv_t *priv = (cx_record_priv_t *)hdl;
    return priv->task_running_flag;
}

int cx_vr_video_open(void *hdl, uint8_t video_name[128])
{
    CHECK_PARAM(hdl && video_name, -1);
    return 0;
}

int cx_vr_video_read(void *hdl, int fd, uint8_t *buf, int max_size)
{
    CHECK_PARAM(hdl && buf, -1);
    return 0;
}

int cx_vr_video_close(void *hdl, int fd)
{
    CHECK_PARAM(hdl, -1);
    return 0;
}

int cx_vr_video_seek(void *hdl, int fd, uint32_t time_stamp)
{
    CHECK_PARAM(hdl, -1);
    return 0;
}